package com.learn.spring.test;

import com.learn.spring.config.TransactionSpringConfig;
import com.learn.spring.service.TransactionService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class case6 {

    /**
     * 1、什么事务
     * (1）事务是数据库操作最基本单元，逻辑上一组操作，要么都成功，如果有一个失败所有操作都失败
     * (2)典型场景:银行转账
     *   lucy转账100元给mary
     *   lucy 少100,mary多100.
     *
     * 2、事务四个特性(ACID)
     * (1) 原子性（A）: 一个操作不可分割，要么都成功要么都不成功
     * (2) 一致性（C）: 前后总量不变，转账前 lucy 和 mary 都有100块，一共200块，转账后 lucy 0 mary 200，一共也是200,
     * (3) 隔离性（I）: 两个事务同时执行，互相补应该影响
     * (4) 持久性（D）: 事务处理完成后一定是会持久保存的
     */

    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans8.xml");
        TransactionService transactionService = context.getBean("transactionService", TransactionService.class);
        transactionService.accountMoney();
    }

    /**
     *  Spring事务管理介绍
     * 1、事务一般添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
     * 2、在 Spring 进行事务管理操作
     *   (1) 有两种方式: 编程式事务管理 和 声明式事务管理(推荐)
     *     编程式通过在：service 通过编码来完成事务，比较麻烦，代码臃肿
     *     声明式事务管理：通过配置申明
     *      1） 注解方式
     *      2） xml配置
     * 3、在 Spring 中声明式事务底层通过 AOP 来完成
     * 4、Spring 事务管理 API
     *   (1）提供一个接口，代表事务管理器，这个接口针对不同的框架提供不同的实现类：
     *    事务管理器接口：public interface PlatformTransactionManager extends TransactionManager
     *    这个项目里用的是 SpringJDBC 做数据库操作，所以实用对应的管理器实现： DataSourceTransactionManager
     */

    /**
     * Spring事务管理操作
     * 1、在spring配置文件配置事务管理器：
     *   使用 SpringJDBC 做数据库操作配套的管理器应该配置如下，如果使用其他视具体情况定
     *   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"></bean>
     * 2、在 spring 配置文件，开启事务注解
     *   (1) 在 spring 配置文件引入名称空间 tx; 见 beans8.xml
     *   (2) 开启事务注解
     * 3、在 Service 类上(或者方法上)添加事务注解 ： @Transactional
     *   如果把这个注解添加类上面，这个类里面所有的方法都添加事务
     *   如果把这个注解添加方法上面，为这个方法添加事务
     */
    @Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans8.xml");
        TransactionService transactionService = context.getBean("transactionService", TransactionService.class);
        transactionService.accountMoneyTransaction();
    }

    /**
     * 申明式事务注解相关参数 @Transactional
     *  1） propagation      : 事务的传播行为
     *    1. 多事务方法调用之间的传播行为。
     *    2. 有七个值可选：
     *      REQUIRED     : 如果 add 方法本身有事务，调用 update 方法之后，update 使用当前 add 方法里面事务
     *                     如果 add 方法本身没有事务，调用 update 方法之后，创建新事务
     *      REQUIRED_NEW : 使用 add 方法调用 update 方法，如果 add 无论是否有事务，都创建新的事务
     *
     *  2） isolation        : 事务的隔离级别
     *      (1) 事务有特性称为隔离性，多事务操作之间不会产生影响。
     *      (2) 不考虑隔离性产生很多问题,有三个读问题:脏读、不可重复读、虚(幻)读
     *        脏读：一个未提交事务可以读到另一个未提交事务的数据，另一个事务发生回滚，读取的事务会造成脏读。
     *        不可重复读： 一个未提交的事务读取到了另一个已提交的事务的修改数据。 这个事一个现象，不是问题。
     *        虚(幻)读： 一个未提交的事务读取到了另一个已提交事务的添加数据。
     *      通过事务隔离性可以解决着3个问题。
     *      百度查看具体细节。
     *
     *  3） timeout          : 超时时间
     *      事务需要在一定时间内进行提交，如果不提交进行回滚；
     *      默认值是-1，-1为不会滚。设置时间以秒单位进行计算
     *  4） readOnly         : 是否只读
     *      (1) 读:查询操作，写:添加修改删除操作
     *      (2) readOnly 默认值 false，表示可以查询，可以添加修改删除操作
     *      (3) 设置 readOnly 值是 true，设置成true之后，只能查询
     *  5） rollbackFor      : 回滚
     *      设置出现哪些异常进行事务回滚
     *  6） noRollbackFor    : 不回滚
     *      设置出现哪些异常不进行事务回滚
     */


    /**
     * 基于XML 的事务实用
     * 1. 配置事务管理起
     * 2. 配置通知
     * 3. 配置切入点与切入面
     */
    @Test
    public void test3() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans9.xml");
        TransactionService transactionService = context.getBean("transactionService", TransactionService.class);
        transactionService.accountMoneyTransactionXml();
    }

    /**
     * 完全注解方式
     */
    @Test
    public void test4() {
        ApplicationContext context = new AnnotationConfigApplicationContext(TransactionSpringConfig.class);
        TransactionService transactionService = context.getBean("transactionService", TransactionService.class);
        transactionService.accountMoneyTransaction();
    }


}
